package com.dx.VegetableOxygenBarBackEnd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dx.VegetableOxygenBarBackEnd.common.ErrorCode;
import com.dx.VegetableOxygenBarBackEnd.exception.BusinessException;
import com.dx.VegetableOxygenBarBackEnd.model.domain.entity.User;
import com.dx.VegetableOxygenBarBackEnd.model.domain.request.CreateUserRequest;
import com.dx.VegetableOxygenBarBackEnd.model.enums.UserRoleType;
import com.dx.VegetableOxygenBarBackEnd.service.UserService;
import com.dx.VegetableOxygenBarBackEnd.mapper.UserMapper;
import com.dx.VegetableOxygenBarBackEnd.utils.AlgorithmUtils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.dx.VegetableOxygenBarBackEnd.constant.UserConstant.USER_LOGIN_STATE;

/**
 * @author dengxiao
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2024-08-14 17:11:16
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private UserMapper userMapper;

    /**
     * 加盐，混淆密码
     */
    private static final String SALT = "zsxqXiao";

    /**
     * 用户注册
     *
     * @param userAccount   账户名
     * @param password      密码
     * @param checkPassword 校验密码
     * @param planetCode    基地编号
     * @return 用户id
     */
    @Override
    public long regestAccount(String userAccount, String password, String checkPassword, String planetCode) {
        //1、校验
        //1.1、判断非空、长度
        if (StringUtils.isAllBlank(userAccount, password, checkPassword, planetCode)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "字段有空的");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号长度小于4");
        }
        if (password.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度应该小于8");
        }
        if (planetCode.length() > 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "基地编号大于5");
        }
        //1.2、判断特殊字符
        String regexPattern = "^[a-zA-Z0-9_.]+$";
        boolean matches = Pattern.matches(regexPattern, userAccount);
        if (!matches) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号存在特殊字符");
        }
        //1.3、校验密码
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        //1.4、数据库校验重复账户名
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        User one = userMapper.selectOne(queryWrapper);
        if (one != null) {
            throw new BusinessException(ErrorCode.REPET_ERROR, "账户重复");
        }
        //1.5、数据库校验重复基地编号
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("planetCode", planetCode);
        User onePlanetCode = userMapper.selectOne(queryWrapper);
        if (onePlanetCode != null) {
            throw new BusinessException(ErrorCode.REPET_ERROR, "基地编号重复");
        }
        //2、密码加密
        String encrypPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        //3、插入用户数据
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encrypPassword);
        user.setPlanetCode(planetCode);
        boolean saveRessult = this.save(user);
        if (!saveRessult) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "插入参数有误");
        }
        return user.getId();
    }

    /**
     * 用户登录
     *
     * @param userAccount        账户名
     * @param password           密码
     * @param httpServletRequest
     * @return 脱敏用户信息
     */
    @Override
    public User userLogin(String userAccount, String password, HttpServletRequest httpServletRequest) {
        //1、校验
        //1.1、判断非空、长度
        if (StringUtils.isAllBlank(userAccount, password)) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号小于4");
        }
        if (password.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度小于8");
        }
        //1.2、判断特殊字符
        String regexPattern = "^[a-zA-Z0-9_.]+$";
        boolean matches = Pattern.matches(regexPattern, userAccount);
        if (!matches) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号存在特殊字符");
        }
        //2、密码加密
        String encrypPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        //3、数据查询账户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encrypPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            log.info("user login failed, userAccount not match userPassword!");
            throw new BusinessException(ErrorCode.NULL_ERROR, "请求账户为空");
        }
        //4、用户脱敏
        User saftyUser = getSaftyUser(user);
        //5、记录登陆状态
        httpServletRequest.getSession().setAttribute(USER_LOGIN_STATE, saftyUser);
        return saftyUser;
    }


    /**
     * 用户脱敏
     *
     * @param user 用户信息
     * @return 脱敏用户信息
     */
    @Override
    public User getSaftyUser(User user) {
        User saftyUser = new User();
        saftyUser.setId(user.getId());
        saftyUser.setUserName(user.getUserName());
        saftyUser.setUserAccount(user.getUserAccount());
        saftyUser.setAvatarUrl(user.getAvatarUrl());
        saftyUser.setGender(user.getGender());
        saftyUser.setUserRole(user.getUserRole());
        saftyUser.setProfile(user.getProfile());
        saftyUser.setPhone(user.getPhone());
        saftyUser.setEmail(user.getEmail());
        saftyUser.setPlanetCode(user.getPlanetCode());
        saftyUser.setCreateTime(user.getCreateTime());
        saftyUser.setUpdateTime(user.getUpdateTime());
        saftyUser.setTags(user.getTags());
        return saftyUser;
    }

    /**
     * 用户注销
     *
     * @param httpServletRequest
     * @return
     */
    @Override
    public int userLogout(HttpServletRequest httpServletRequest) {
        httpServletRequest.getSession().removeAttribute(USER_LOGIN_STATE);
        return 1;
    }

    @Override
    public long createUser(CreateUserRequest createUserRequest) {
        //1、校验
        //1.1、判断非空、长度
        if (StringUtils.isAllBlank(
                createUserRequest.getUserAccount(),
                createUserRequest.getUserPassword(),
                createUserRequest.getCheckPassword(),
                createUserRequest.getPlanetCode())) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "必填字段字段有空的");
        }
        if (createUserRequest.getUserAccount().length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号长度小于4");
        }
        if (createUserRequest.getUserPassword().length() < 8 || createUserRequest.getCheckPassword().length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度小于8");
        }
        if (createUserRequest.getPlanetCode().length() > 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "基地编号大于5");
        }
        //1.2、判断特殊字符
        String regexPattern = "^[a-zA-Z0-9_.]+$";
        boolean matches = Pattern.matches(regexPattern, createUserRequest.getUserAccount());
        if (!matches) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号存在特殊字符");
        }
        //1.3、校验密码
        if (!createUserRequest.getUserPassword().equals(createUserRequest.getCheckPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        //1.4、数据库校验重复账户名
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", createUserRequest.getUserAccount());
        User one = userMapper.selectOne(queryWrapper);
        if (one != null) {
            throw new BusinessException(ErrorCode.REPET_ERROR, "账户已经存在");
        }
        //1.5、数据库校验重复基地编号
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("planetCode", createUserRequest.getPlanetCode());
        User onePlanetCode = userMapper.selectOne(queryWrapper);
        if (onePlanetCode != null) {
            throw new BusinessException(ErrorCode.REPET_ERROR, "基地编号重复");
        }
        //2、密码加密
        String encrypPassword = DigestUtils.md5DigestAsHex((SALT + createUserRequest.getUserPassword()).getBytes());
        //3、插入用户数据
        User user = new User();
        user.setUserAccount(createUserRequest.getUserAccount());
        user.setUserPassword(encrypPassword);
        user.setUserRole(createUserRequest.getUserRole());
        user.setPlanetCode(createUserRequest.getPlanetCode());

        boolean saveRessult = this.save(user);
        if (!saveRessult) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "插入参数有误");
        }
        return user.getId();
    }

    /**
     * 根据标签搜索用户(内存过滤)
     *
     * @param tagNameList 用户拥有的标签
     * @return 用户列表
     */
    @Override
    public List<User> searchUserByTags(List<String> tagNameList) {
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //内存查询，将数据读出来，在这里进行处理，（灵活）
        //1、先查询所有用户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        List<User> userList = userMapper.selectList(queryWrapper);
        Gson gson = new Gson();
        //2、在内存中判断是否含有标签
        return userList.stream().filter(user -> {
            String tags = user.getTags();
            if (StringUtils.isAllBlank(tags)) {
                return false;
            }
            Set<String> temptagNameSet = gson.fromJson(tags, new TypeToken<Set<String>>() {
            }.getType());
            temptagNameSet = Optional.ofNullable(temptagNameSet).orElse(new HashSet<>());
            for (String tagName : tagNameList) {
                if (!temptagNameSet.contains(tagName)) {
                    return false;
                }
            }
            return true;
        }).map(this::getSaftyUser).collect(Collectors.toList());
    }


    /**
     * 根据标签搜索用户(sql查询)
     *
     * @param tagNameList 用户拥有的标签
     * @return 用户列表
     */
    @Deprecated
    private List<User> searchUserByTagsBySQL(List<String> tagNameList) {
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //数据库查询，数据库模糊查询（操作简单）
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        for (String tagname : tagNameList) {
            queryWrapper = queryWrapper.like("tags", tagname);
        }
        List<User> users = userMapper.selectList(queryWrapper);
        return users.stream().map(this::getSaftyUser).collect(Collectors.toList());
    }
    /**分页
     * 根据标签搜索用户(内存过滤)
     *
     * @param tagNameList 用户拥有的标签
     * @return 用户列表
     */
    @Override
    public Page<User> searchUserByTagsBySQLWithPage(Page<User> page, List<String> tagNameList) {
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 构建查询条件，使用 like 进行模糊匹配
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        for (String tagname : tagNameList) {
            queryWrapper = queryWrapper.like("tags", tagname);
        }

        // 使用分页查询
        Page<User> userPage = userMapper.selectPage(page, queryWrapper);

        // 对查询结果进行处理，映射为安全的用户数据
        List<User> safeUserList = userPage.getRecords().stream()
                .map(this::getSaftyUser)
                .collect(Collectors.toList());

        // 将处理后的安全用户数据重新设置到分页对象中
        userPage.setRecords(safeUserList);

        return userPage;
    }



    /**
     * 鉴权，是否为管理员
     *
     * @param httpServletRequest
     * @return
     */
    @Override
    public boolean isAdminUser(HttpServletRequest httpServletRequest){
        Object userobj = httpServletRequest.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userobj;
        if(user == null){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }

        boolean b = UserRoleType.ADMIN_USER.equalsValue(user.getUserRole());
        return b;
    }
    /**
     * 鉴权，是否为管理员
     *
     * @param loginUser
     * @return
     */
    @Override
    public boolean isAdminUser(User loginUser){
        if(loginUser == null){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        boolean b = UserRoleType.ADMIN_USER.equalsValue(loginUser.getUserRole());
        return b;
    }

    /**
     * 用户更新
     * @param user
     * @return
     */
    @Override
    public int updateUser(User user,User loginUser) {
        long userId = user.getId();
        if(userId <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //如果是管理员可以更新所有信息
        //如果是用户，只能更新自己的信息
        if(!isAdminUser(loginUser) && userId != loginUser.getId()){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        User selectById = userMapper.selectById(userId);
        if(selectById == null){
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        return userMapper.updateById(user);
    }
    /**
     * 获取当前登录用户
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest httpServletRequest) {
        if(httpServletRequest == null){
            return null;
        }
        Object currentUser = httpServletRequest.getSession().getAttribute(USER_LOGIN_STATE);
        if(currentUser == null){
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        return (User) currentUser;
    }
    /**
     * 获取匹配度最高的用户
     * @param num
     * @param loginUser
     */
    @Override
    public List<User> matchUsers(long num, User loginUser) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "tags");
        queryWrapper.isNotNull("tags");
        List<User> userList = this.list(queryWrapper);

        String tags = loginUser.getTags();
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(tags, new TypeToken<List<String>>() {}.getType());

        // 用户列表的下标 =》 相似度
        List<Pair<User, Long>> list = new ArrayList<>();

        for (User user : userList) {
            String userTags = user.getTags();
            // 无标签或过滤自己
            if (StringUtils.isBlank(userTags) || user.getId().equals(loginUser.getId())) {
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {}.getType());
            // 计算距离，距离越近，得分越高
            long distance = AlgorithmUtils.minDistance(tagList, userTagList);
            list.add(Pair.of(user, distance)); // 使用Pair.of()创建Pair实例
        }
        //按距离由小到大排序
        List<Pair<User, Long>> topUserPairList = list.stream()
                .sorted(Comparator.comparingLong(Pair::getValue))
                .limit(num)
                .collect(Collectors.toList());
        //获取用户的信息id列表\原本顺序的userId顺序列表
        List<Long> userIdList = topUserPairList.stream()
                .map(pair ->pair.getKey().getId()) // 提取User对象
                .collect(Collectors.toList());
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.in("id",userIdList);
        //根据id进行分组，便于之后使用id映射value值，，，，id=>value
        Map<Long, List<User>> userIdListMap = this.list(userQueryWrapper)
                .stream()
                .map(user -> getSaftyUser(user))
                .collect(Collectors.groupingBy(User::getId));
        List<User> finalUserList = new ArrayList<>();
        //iter 快捷键
        //遍历顺序的id列表，在最终列表进行add，相当于根据这个用户id的顺序进行add对应的value值
        for (Long userId : userIdList) {
            finalUserList.add(userIdListMap.get(userId).get(0));
        }
        return finalUserList;
    }

}



